package com.kuangstudy.controller;

import com.alibaba.fastjson.JSON;
import com.kuangstudy.constant.RedisConstants;
import com.kuangstudy.entity.Category;
import com.kuangstudy.service.category.ICategoryService;
import io.rebloom.client.Client;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author panghu
 * @version 1.0.0
 * @createTime 2023年09月20日 10:52:00
 * @Description
 */
@RestController
@Api(tags = "课程业务")
public class CategoryController {

    @Autowired
    private ICategoryService categoryService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private Client rebloomClient;


    /**
     * 查询课程分类信息
     *
     * @param cid
     * @return
     */
    @PostMapping("/find/categorys/{cid}")
    @ApiOperation("查询课程分类信息【设置默认值】")
    public List<Category> findCategorysInfo(@PathVariable("cid") Integer cid) {
        // 1、判断分类id是否存在
        if (Objects.isNull(cid)) {
            throw new RuntimeException("分类ID不允许为空!!!");
        }

        List<Category> categoryList = new ArrayList<>();

        // 2、先从缓存中获取分类的信息，如果存在直接走缓存；如果不存在先走数据库查询再放入到redis缓存中
        String categoryListStr = (String) this.redisTemplate.opsForValue().get(RedisConstants.COURSE_CATEGORYS_STRING + cid);

        if (StringUtils.isBlank(categoryListStr)) {
            // 缓存中如果不存在，从数据库查询再放入到redis的缓存当中
            categoryList = categoryService.findCategorysByPid(cid);
            if (!CollectionUtils.isEmpty(categoryList)) {
                this.redisTemplate.opsForValue().set(RedisConstants.COURSE_CATEGORYS_STRING + cid, JSON.toJSONString(categoryList));
            } else {
                // 如果你查询的id对应的key，如果数据库中没有，我们也在缓存中放一份，放入的值空数组([])、空集合([])、空对象(null)等。 todo 不让你直接冲到数据库中，记得给过期时间戳
                this.redisTemplate.opsForValue().set(RedisConstants.COURSE_CATEGORYS_STRING + cid, JSON.toJSONString(categoryList), 5, TimeUnit.MINUTES);
            }
        } else {
            // 缓存中存在
            categoryList = JSON.parseArray(categoryListStr, Category.class);
        }
        return categoryList;
    }


    /**
     * 查询课程分类信息
     *
     * @param cid
     * @return
     */
    @PostMapping("/select/categorys/{cid}")
    @ApiOperation("查询课程分类信息【布隆过滤器+默认值兜底】")
    public List<Category> findCategorysInfo2(@PathVariable("cid") Integer cid) {
        // 1、判断分类id是否存在
        if (Objects.isNull(cid)) {
            throw new RuntimeException("分类ID不允许为空!!!");
        }

        // 使用布隆过滤器解决缓存穿透的问题 bf.madd redis:bloom:course:category 0 1 2 3 4 5
        // 进行bloom过滤：bd.mexists redis:bloom:course:category
        boolean[] booleans = rebloomClient.existsMulti(RedisConstants.REDIS_BLOOM_COURSE_CATEGORY, cid.toString());
        // 如果cid没有在布隆过滤器中，直接返回
        if (!booleans[0]){
            throw new RuntimeException("分类ID不允许为空!!!");
        }

        List<Category> categoryList = new ArrayList<>();

        // 2、先从缓存中获取分类的信息，如果存在直接走缓存；如果不存在先走数据库查询再放入到redis缓存中
        String categoryListStr = (String) this.redisTemplate.opsForValue().get(RedisConstants.COURSE_CATEGORYS_STRING + cid);

        if (StringUtils.isBlank(categoryListStr)) {
            // 缓存中如果不存在，从数据库查询再放入到redis的缓存当中 todo 存在缓存穿透的问题
            categoryList = categoryService.findCategorysByPid(cid);
            if (!CollectionUtils.isEmpty(categoryList)){
                this.redisTemplate.opsForValue().set(RedisConstants.COURSE_CATEGORYS_STRING + cid, JSON.toJSONString(categoryList));
            }else {
                // todo bloom和设置默认值一起用，做兜底方法（因为bloom有误差）
                // 如果你查询的id对应的key，如果数据库中没有，我们也在缓存中放一份，放入的值空数组([])、空集合([])、空对象(null)等。 todo 不让你直接冲到数据库中，记得给过期时间戳
                this.redisTemplate.opsForValue().set(RedisConstants.COURSE_CATEGORYS_STRING + cid, JSON.toJSONString(categoryList), 5, TimeUnit.MINUTES);
            }
        } else {
            // 缓存中存在
            categoryList = JSON.parseArray(categoryListStr, Category.class);
        }
        return categoryList;
    }


}
